/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package skyproc;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.zip.DataFormatException;
import lev.LExporter;
import lev.LFlags;
import lev.LStream;
import skyproc.exceptions.BadParameter;
import skyproc.exceptions.BadRecord;

/**
 * Weapon Records
 *
 * @author Justin Swanson
 */
public class WEAP extends MajorRecordDescription {

    private static final Type[] type = {Type.WEAP};
    SubForm BAMT = new SubForm(Type.BAMT);
    SubForm BIDS = new SubForm(Type.BIDS);
    SubForm CNAM = new SubForm(Type.CNAM);
    CRDT CRDT = new CRDT();
    DATA DATA = new DATA();
    DNAM DNAM = new DNAM();
    SubData EAMT = new SubData(Type.EAMT);
    SubForm EITM = new SubForm(Type.EITM);
    SubForm ETYP = new SubForm(Type.ETYP);
    SubForm INAM = new SubForm(Type.INAM);
    /**
     *
     */
    public KeywordSet keywords = new KeywordSet();
    SubData OBND = new SubData(Type.OBND);
    SubString MODL = new SubString(Type.MODL, true);
    SubData MODS = new SubData(Type.MODS);
    SubData MODT = new SubData(Type.MODT);
    SubForm NAM8 = new SubForm(Type.NAM8);
    SubForm NAM9 = new SubForm(Type.NAM9);
    SubString NNAM = new SubString(Type.NNAM, true);
    SubForm SNAM = new SubForm(Type.SNAM);
    SubForm TNAM = new SubForm(Type.TNAM);
    SubForm UNAM = new SubForm(Type.UNAM);
    /**
     * A script package containing scripts and their properties
     */
    public ScriptPackage scripts = new ScriptPackage();
    SubData VNAM = new SubData(Type.VNAM);
    SubForm WNAM = new SubForm(Type.WNAM);

    WEAP() {
        super();
        subRecords.remove(Type.FULL);
        subRecords.remove(Type.DESC);

        subRecords.add(scripts);
        subRecords.add(OBND);
        subRecords.add(FULL);
        subRecords.add(MODL);
        subRecords.add(MODT);
        subRecords.add(EITM);
        subRecords.add(EAMT);
        subRecords.add(MODS);
        subRecords.add(ETYP);
        subRecords.add(BIDS);
        subRecords.add(BAMT);
        subRecords.add(keywords);
        subRecords.add(description);
        subRecords.add(NNAM);
        subRecords.add(INAM);
        subRecords.add(SNAM);
        subRecords.add(WNAM);
        subRecords.add(TNAM);
        subRecords.add(UNAM);
        subRecords.add(NAM9);
        subRecords.add(NAM8);
        subRecords.add(DATA);
        subRecords.add(DNAM);
        subRecords.add(CRDT);
        subRecords.add(VNAM);
        subRecords.add(CNAM);
    }

    @Override
    Type[] getTypes() {
        return type;
    }

    @Override
    Record getNew() {
        return new WEAP();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final WEAP other = (WEAP) obj;
        if (!Objects.equals(this.BAMT, other.BAMT)) {
            return false;
        }
        if (!Objects.equals(this.BIDS, other.BIDS)) {
            return false;
        }
        if (!Objects.equals(this.CNAM, other.CNAM)) {
            return false;
        }
        if (!Objects.equals(this.CRDT, other.CRDT)) {
            return false;
        }
        if (!Objects.equals(this.DATA, other.DATA)) {
            return false;
        }
        if (!Objects.equals(this.DNAM, other.DNAM)) {
            return false;
        }
        if (!Objects.equals(this.EAMT, other.EAMT)) {
            return false;
        }
        if (!Objects.equals(this.EITM, other.EITM)) {
            return false;
        }
        if (!Objects.equals(this.ETYP, other.ETYP)) {
            return false;
        }
        if (!Objects.equals(this.INAM, other.INAM)) {
            return false;
        }
        if (!Objects.equals(this.keywords, other.keywords)) {
            return false;
        }
        if (!Objects.equals(this.OBND, other.OBND)) {
            return false;
        }
        if (!Objects.equals(this.MODL, other.MODL)) {
            return false;
        }
        if (!Objects.equals(this.MODS, other.MODS)) {
            return false;
        }
        if (!Objects.equals(this.MODT, other.MODT)) {
            return false;
        }
        if (!Objects.equals(this.NAM8, other.NAM8)) {
            return false;
        }
        if (!Objects.equals(this.NAM9, other.NAM9)) {
            return false;
        }
        if (!Objects.equals(this.NNAM, other.NNAM)) {
            return false;
        }
        if (!Objects.equals(this.SNAM, other.SNAM)) {
            return false;
        }
        if (!Objects.equals(this.TNAM, other.TNAM)) {
            return false;
        }
        if (!Objects.equals(this.UNAM, other.UNAM)) {
            return false;
        }
        if (!Objects.equals(this.scripts, other.scripts)) {
            return false;
        }
        if (!Objects.equals(this.VNAM, other.VNAM)) {
            return false;
        }
        if (!Objects.equals(this.WNAM, other.WNAM)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + Objects.hashCode(this.BAMT);
        hash = 29 * hash + Objects.hashCode(this.BIDS);
        hash = 29 * hash + Objects.hashCode(this.CNAM);
        hash = 29 * hash + Objects.hashCode(this.CRDT);
        hash = 29 * hash + Objects.hashCode(this.DATA);
        hash = 29 * hash + Objects.hashCode(this.DNAM);
        hash = 29 * hash + Objects.hashCode(this.EAMT);
        hash = 29 * hash + Objects.hashCode(this.EITM);
        hash = 29 * hash + Objects.hashCode(this.ETYP);
        hash = 29 * hash + Objects.hashCode(this.INAM);
        hash = 29 * hash + Objects.hashCode(this.keywords);
        hash = 29 * hash + Objects.hashCode(this.OBND);
        hash = 29 * hash + Objects.hashCode(this.MODL);
        hash = 29 * hash + Objects.hashCode(this.MODS);
        hash = 29 * hash + Objects.hashCode(this.MODT);
        hash = 29 * hash + Objects.hashCode(this.NAM8);
        hash = 29 * hash + Objects.hashCode(this.NAM9);
        hash = 29 * hash + Objects.hashCode(this.NNAM);
        hash = 29 * hash + Objects.hashCode(this.SNAM);
        hash = 29 * hash + Objects.hashCode(this.TNAM);
        hash = 29 * hash + Objects.hashCode(this.UNAM);
        hash = 29 * hash + Objects.hashCode(this.scripts);
        hash = 29 * hash + Objects.hashCode(this.VNAM);
        hash = 29 * hash + Objects.hashCode(this.WNAM);
        return hash;
    }
    
    @Override
    public MajorRecord merge(MajorRecord no, MajorRecord bo) {
        super.merge(no, bo);
        WEAP e = this;
        if (!(no == null && bo == null && (no instanceof WEAP) && (bo instanceof WEAP))) {
            final WEAP ne = (WEAP) no;
            final WEAP be = (WEAP) bo;
            SubRecords sList = e.subRecords;
            SubRecords nsList = ne.subRecords;
            SubRecords bsList = be.subRecords;
            for (SubRecord s : sList) {
                s.merge(nsList.get(s.type[0]), bsList.get(s.type[0]));
            }
        }
        return e;
    }

    class DNAM extends SubRecord {

        WeaponType wtype;
        byte[] unknown1;
        float speed;
        float reach;
        LFlags flags1 = new LFlags(4);
        float sightFOV;
        byte[] unknown2;
        int vats;
        byte[] unknown3;
        int numProjectiles;
        int embeddedWeapActorValue;
        float minRange;
        float maxRange;
        byte[] unknown5;
        LFlags flags2 = new LFlags(4);
        byte[] unknown6;
        LFlags flags3 = new LFlags(4);
        byte[] unknown7;
        byte[] resist;
        byte[] unknown8;
        float stagger;

        public DNAM() {
            super(Type.DNAM);
        }

        @Override
        void export(LExporter out, Mod srcMod) throws IOException {
            super.export(out, srcMod);
            out.write(wtype.ordinal(), 1);
            out.write(unknown1, 3);
            out.write(speed);
            out.write(reach);
            out.write(flags1.export());
            out.write(sightFOV);
            out.write(unknown2, 4);
            out.write(vats, 1);
            out.write(unknown3, 1);
            out.write(numProjectiles, 1);
            out.write(embeddedWeapActorValue, 1);
            out.write(minRange);
            out.write(maxRange);
            out.write(unknown5, 4);
            out.write(flags2.export());
            out.write(unknown6, 24);
            out.write(flags3.export());
            out.write(unknown7, 16);
            out.write(resist, 4);
            out.write(unknown8, 4);
            out.write(stagger);
        }

        @Override
        void parseData(LStream in) throws BadRecord, DataFormatException, BadParameter, IOException {
            super.parseData(in);
            wtype = WeaponType.values()[in.extractInt(1)];
            unknown1 = in.extract(3);
            speed = in.extractFloat();
            reach = in.extractFloat();
            flags1.set(in.extract(4));
            sightFOV = in.extractFloat();
            unknown2 = in.extract(4);
            vats = in.extractInt(1);
            unknown3 = in.extract(1);
            numProjectiles = in.extractInt(1);
            embeddedWeapActorValue = in.extractInt(1);
            minRange = in.extractFloat();
            maxRange = in.extractFloat();
            unknown5 = in.extract(4);
            flags2.set(in.extract(4));
            unknown6 = in.extract(24);
            flags3.set(in.extract(4));
            unknown7 = in.extract(16);
            resist = in.extract(4);
            unknown8 = in.extract(4);
            stagger = in.extractFloat();
            if (logging()) {
                logSync("", "WType: " + wtype + ", speed: " + speed + ", reach: " + reach);
                logSync("", "SightFOV: " + sightFOV + ", vats: " + vats + ", numProjectiles: " + numProjectiles);
                logSync("", "EmbeddedWeapActorVal: " + embeddedWeapActorValue + ", MinRange: " + minRange + ", MaxRange: " + maxRange);
                logSync("", "stagger: " + stagger + ", Bound: " + get(WeaponFlag.BoundWeapon) + ", Cant Drop: " + get(WeaponFlag.CantDrop));
                logSync("", "Hide Backpack: " + get(WeaponFlag.HideBackpack) + ", Ignore Normal Weapon Resistance: " + get(WeaponFlag.IgnoresNormalWeaponResistance) + ", Minor Crime: " + get(WeaponFlag.MinorCrime));
                logSync("", "NPCs Use Ammo: " + get(WeaponFlag.NPCsUseAmmo) + ", No jam after reload: " + get(WeaponFlag.NoJamAfterReload) + ", Non Hostile: " + get(WeaponFlag.NonHostile));
                logSync("", "Non Playable: " + get(WeaponFlag.NonPlayable) + ", Not used in normal combat: " + get(WeaponFlag.NotUsedInNormalCombat) + ", Player Only: " + get(WeaponFlag.PlayerOnly));
            }
        }

        @Override
        SubRecord getNew(Type type) {
            return new DNAM();
        }

        @Override
        Boolean isValid() {
            return true;
        }

	@Override
	int getContentLength(Mod srcMod) {
	    return 100;
	}

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final DNAM other = (DNAM) obj;
            if (this.wtype != other.wtype) {
                return false;
            }
            if (!Arrays.equals(this.unknown1, other.unknown1)) {
                return false;
            }
            if (Float.floatToIntBits(this.speed) != Float.floatToIntBits(other.speed)) {
                return false;
            }
            if (Float.floatToIntBits(this.reach) != Float.floatToIntBits(other.reach)) {
                return false;
            }
            if (!Objects.equals(this.flags1, other.flags1)) {
                return false;
            }
            if (Float.floatToIntBits(this.sightFOV) != Float.floatToIntBits(other.sightFOV)) {
                return false;
            }
            if (!Arrays.equals(this.unknown2, other.unknown2)) {
                return false;
            }
            if (this.vats != other.vats) {
                return false;
            }
            if (!Arrays.equals(this.unknown3, other.unknown3)) {
                return false;
            }
            if (this.numProjectiles != other.numProjectiles) {
                return false;
            }
            if (this.embeddedWeapActorValue != other.embeddedWeapActorValue) {
                return false;
            }
            if (Float.floatToIntBits(this.minRange) != Float.floatToIntBits(other.minRange)) {
                return false;
            }
            if (Float.floatToIntBits(this.maxRange) != Float.floatToIntBits(other.maxRange)) {
                return false;
            }
            if (!Arrays.equals(this.unknown5, other.unknown5)) {
                return false;
            }
            if (!Objects.equals(this.flags2, other.flags2)) {
                return false;
            }
            if (!Arrays.equals(this.unknown6, other.unknown6)) {
                return false;
            }
            if (!Objects.equals(this.flags3, other.flags3)) {
                return false;
            }
            if (!Arrays.equals(this.unknown7, other.unknown7)) {
                return false;
            }
            if (!Arrays.equals(this.resist, other.resist)) {
                return false;
            }
            if (!Arrays.equals(this.unknown8, other.unknown8)) {
                return false;
            }
            if (Float.floatToIntBits(this.stagger) != Float.floatToIntBits(other.stagger)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 97 * hash + (this.wtype != null ? this.wtype.hashCode() : 0);
            hash = 97 * hash + Arrays.hashCode(this.unknown1);
            hash = 97 * hash + Float.floatToIntBits(this.speed);
            hash = 97 * hash + Float.floatToIntBits(this.reach);
            hash = 97 * hash + Objects.hashCode(this.flags1);
            hash = 97 * hash + Float.floatToIntBits(this.sightFOV);
            hash = 97 * hash + Arrays.hashCode(this.unknown2);
            hash = 97 * hash + this.vats;
            hash = 97 * hash + Arrays.hashCode(this.unknown3);
            hash = 97 * hash + this.numProjectiles;
            hash = 97 * hash + this.embeddedWeapActorValue;
            hash = 97 * hash + Float.floatToIntBits(this.minRange);
            hash = 97 * hash + Float.floatToIntBits(this.maxRange);
            hash = 97 * hash + Arrays.hashCode(this.unknown5);
            hash = 97 * hash + Objects.hashCode(this.flags2);
            hash = 97 * hash + Arrays.hashCode(this.unknown6);
            hash = 97 * hash + Objects.hashCode(this.flags3);
            hash = 97 * hash + Arrays.hashCode(this.unknown7);
            hash = 97 * hash + Arrays.hashCode(this.resist);
            hash = 97 * hash + Arrays.hashCode(this.unknown8);
            hash = 97 * hash + Float.floatToIntBits(this.stagger);
            return hash;
        }
        
        @Override
        public SubRecord merge(SubRecord no, SubRecord bo) {
            DNAM e = this;
            if (!(no == null && bo == null && (no instanceof DNAM) && (bo instanceof DNAM))) {
                final DNAM ne = (DNAM) no;
                final DNAM be = (DNAM) bo;
                SkyBash.merge(e.wtype, ne.wtype, be.wtype, type[0], "weapon type");
                SkyBash.merge(e.unknown1, ne.unknown1, be.unknown1, type[0], "unknown");
                SkyBash.merge(e.speed, ne.speed, be.speed, type[0], "speed");
                SkyBash.merge(e.reach, ne.reach, be.reach, type[0], "reach");
                SkyBash.merge(e.sightFOV, ne.sightFOV, be.sightFOV, type[0], "sight FOV");
                SkyBash.merge(e.unknown2, ne.unknown2, be.unknown2, type[0], "unknown");
                SkyBash.merge(e.vats, ne.vats, be.vats, type[0], "VATS?");
                SkyBash.merge(e.unknown3, ne.unknown3, be.unknown3, type[0], "unknown");
                SkyBash.merge(e.numProjectiles, ne.numProjectiles, be.numProjectiles, type[0], "number of projectiles");
                SkyBash.merge(e.embeddedWeapActorValue, ne.embeddedWeapActorValue, be.embeddedWeapActorValue, type[0], "embedded weapon actor value");
                SkyBash.merge(e.minRange, ne.minRange, be.minRange, type[0], "min range");
                SkyBash.merge(e.maxRange, ne.maxRange, be.maxRange, type[0], "max range");
                SkyBash.merge(e.unknown5, ne.unknown5, be.unknown5, type[0], "unknown");
                SkyBash.merge(e.unknown6, ne.unknown6, be.unknown6, type[0], "unknown");
                SkyBash.merge(e.unknown7, ne.unknown7, be.unknown7, type[0], "unknown");
                SkyBash.merge(e.resist, ne.resist, be.resist, type[0], "resist");
                SkyBash.merge(e.unknown8, ne.unknown8, be.unknown8, type[0], "unknown");
                SkyBash.merge(e.stagger, ne.stagger, be.stagger, type[0], "stagger");
                e.flags1.merge(ne.flags1, be.flags1, type[0]);
                e.flags2.merge(ne.flags2, be.flags2, type[0]);
                e.flags3.merge(ne.flags3, be.flags3, type[0]);
            }
            return e;
        }
    }

    static class DATA extends SubRecord {

        int value = 0;
        float weight = 0;
        int damage = 0;

        public DATA() {
            super(Type.DATA);
        }

        @Override
        void export(LExporter out, Mod srcMod) throws IOException {
            super.export(out, srcMod);
            out.write(value);
            out.write(weight);
            out.write(damage, 2);
        }

        @Override
        void parseData(LStream in) throws BadRecord, DataFormatException, BadParameter, IOException {
            super.parseData(in);
            value = in.extractInt(4);
            weight = in.extractFloat();
            damage = in.extractInt(2);
            if (logging()) {
                logSync("", "Value: " + value + ", weight: " + weight + ", damage: " + damage);
            }
        }

        @Override
        SubRecord getNew(Type type) {
            return new DATA();
        }

        @Override
        Boolean isValid() {
            return true;
        }

	@Override
	int getContentLength(Mod srcMod) {
	    return 10;
	}

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final DATA other = (DATA) obj;
            if (this.value != other.value) {
                return false;
            }
            if (Float.floatToIntBits(this.weight) != Float.floatToIntBits(other.weight)) {
                return false;
            }
            if (this.damage != other.damage) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 19 * hash + this.value;
            hash = 19 * hash + Float.floatToIntBits(this.weight);
            hash = 19 * hash + this.damage;
            return hash;
        }
        
        @Override
        public SubRecord merge(SubRecord no, SubRecord bo) {
            DATA e = this;
            if (!(no == null && bo == null && (no instanceof DATA) && (bo instanceof DATA))) {
                final DATA ne = (DATA) no;
                final DATA be = (DATA) bo;
                SkyBash.merge(e.value, ne.value, be.value, type[0], "value");
                SkyBash.merge(e.weight, ne.weight, be.weight, type[0], "weight");
                SkyBash.merge(e.damage, ne.damage, be.damage, type[0], "damage");
            }
            return e;
        }
    }

    static class CRDT extends SubRecord {

        int critDmg;
        byte[] unknown0;
        float critMult;
        int onDeath;
        byte[] unknown;
        FormID critEffect = new FormID();

        public CRDT() {
            super(Type.CRDT);
        }

        @Override
        void export(LExporter out, Mod srcMod) throws IOException {
            super.export(out, srcMod);
            out.write(critDmg);
            out.write(critMult);
            out.write(onDeath, 1);
            out.write(unknown, 3);
            critEffect.export(out);
        }

        @Override
        void parseData(LStream in) throws BadRecord, DataFormatException, BadParameter, IOException {
            super.parseData(in);
            critDmg = in.extractInt(2);
            unknown0 = in.extract(2);
            critMult = in.extractFloat();
            onDeath = in.extractInt(1);
            unknown = in.extract(3);
            critEffect.setInternal(in.extract(4));
            if (logging()) {
                logSync("", "critDmg: " + critDmg + ", critMult: " + critMult + ", crit effect: " + critEffect);
            }
        }

        @Override
        SubRecord getNew(Type type) {
            return new CRDT();
        }

        @Override
        Boolean isValid() {
            return true;
        }

        @Override
        int getContentLength(Mod srcMod) {
            return 16;
        }

	@Override
	ArrayList<FormID> allFormIDs () {
	    ArrayList<FormID> out = new ArrayList<FormID>(1);
	    out.add(critEffect);
	    return out;
	}

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final CRDT other = (CRDT) obj;
            if (this.critDmg != other.critDmg) {
                return false;
            }
            if (!Arrays.equals(this.unknown0, other.unknown0)) {
                return false;
            }
            if (Float.floatToIntBits(this.critMult) != Float.floatToIntBits(other.critMult)) {
                return false;
            }
            if (this.onDeath != other.onDeath) {
                return false;
            }
            if (!Arrays.equals(this.unknown, other.unknown)) {
                return false;
            }
            if (!Objects.equals(this.critEffect, other.critEffect)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 89 * hash + this.critDmg;
            hash = 89 * hash + Arrays.hashCode(this.unknown0);
            hash = 89 * hash + Float.floatToIntBits(this.critMult);
            hash = 89 * hash + this.onDeath;
            hash = 89 * hash + Arrays.hashCode(this.unknown);
            hash = 89 * hash + Objects.hashCode(this.critEffect);
            return hash;
        }
        
        @Override
        public SubRecord merge(SubRecord no, SubRecord bo) {
            CRDT e = this;
            if (!(no == null && bo == null && (no instanceof CRDT) && (bo instanceof CRDT))) {
                final CRDT ne = (CRDT) no;
                final CRDT be = (CRDT) bo;
                SkyBash.merge(e.critDmg, ne.critDmg, be.critDmg, type[0], "critical damage");
                SkyBash.merge(e.unknown0, ne.unknown0, be.unknown0, type[0], "unknown");
                SkyBash.merge(e.critMult, ne.critMult, be.critMult, type[0], "critical multiplier");
                SkyBash.merge(e.onDeath, ne.onDeath, be.onDeath, type[0], "on death (?)");
                SkyBash.merge(e.unknown, ne.unknown, be.unknown, type[0], "unknown");
                e.critEffect.merge(ne.critEffect, be.critEffect, type[0]);
            }
            return e;
        }
    }

    /**
     * An enum to represent to Weapon Type options
     */
    public enum WeaponType {

        /**
         *
         */
        Projectile,
        /**
         *
         */
        OneHSword,
        /**
         *
         */
        Dagger,
        /**
         *
         */
        OneHAxe,
        /**
         *
         */
        OneHBlunt,
        /**
         *
         */
        TwoHSword,
        /**
         *
         */
        TwoHBluntAxe,
        /**
         *
         */
        Bow,
        /**
         *
         */
        Staff
    }

    /**
     *
     */
    public enum WeaponFlag {

        /**
         *
         */
        IgnoresNormalWeaponResistance(1, 0),
        /**
         *
         */
        HideBackpack(4, 0),
        /**
         *
         */
        NonPlayable(7, 0),
        /**
         *
         */
        CantDrop(3, 0),
        /**
         *
         */
        PlayerOnly(0, 1),
        /**
         *
         */
        NPCsUseAmmo(1, 1),
        /**
         *
         */
        NoJamAfterReload(3, 1),
        /**
         *
         */
        MinorCrime(4, 1),
        /**
         *
         */
        NotUsedInNormalCombat(6, 1),
        /**
         *
         */
        NonHostile(8, 1),
        /**
         *
         */
        BoundWeapon(13, 1),;
        int value;
        int flagSet;

        WeaponFlag(int value, int flagSet) {
            this.value = value;
            this.flagSet = flagSet;
        }
    }

    // Get /set
    /**
     *
     * @param value
     */
    public void setValue(int value) {
        DATA.value = Math.abs(value);
    }

    /**
     *
     * @return
     */
    public int getValue() {
        return DATA.value;
    }

    /**
     *
     * @param weight
     */
    public void setWeight(float weight) {
        DATA.weight = weight;
    }

    /**
     *
     * @return
     */
    public float getWeight() {
        return DATA.weight;
    }

    /**
     *
     * @param damage
     */
    public void setDamage(int damage) {
        DATA.damage = Math.abs(damage) % 0xFFFF;  // can't be more than 2 bytes
    }

    /**
     *
     * @param amount
     */
    public void setEnchantmentCharge(int amount) {
        EAMT.setDataAbs(amount, 2, 2);
    }

    /**
     *
     * @return
     */
    public int getEnchantmentCharge() {
        return EAMT.toInt();
    }

    /**
     *
     * @param id
     */
    public void setEnchantment(FormID id) {
        EITM.setForm(id);
    }

    /**
     *
     * @return
     */
    public FormID getEnchantment() {
        return EITM.getForm();
    }

    /**
     *
     * @param id
     */
    public void setEquipmentSlot(FormID id) {
        ETYP.setForm(id);
    }

    /**
     *
     * @return
     */
    public FormID getEquipmentSlot() {
        return ETYP.getForm();
    }

    /**
     *
     * @param id
     */
    public void setImpactSet(FormID id) {
        INAM.setForm(id);
    }

    /**
     *
     * @return
     */
    public FormID getImpactSet() {
        return INAM.getForm();
    }

    /**
     *
     * @param filename
     */
    public void setModelFilename(String filename) {
        MODL.setString(filename);
    }

    /**
     *
     * @return
     */
    public String getModelFilename() {
        return MODL.print();
    }

    /**
     *
     * @param id
     */
    public void setSheathSound(FormID id) {
        NAM8.setForm(id);
    }

    /**
     *
     * @return
     */
    public FormID getSheathSound() {
        return NAM8.getForm();
    }

    /**
     *
     * @param id
     */
    public void setDrawSound(FormID id) {
        NAM9.setForm(id);
    }

    /**
     *
     * @return
     */
    public FormID getDrawSound() {
        return NAM9.getForm();
    }

    /**
     *
     * @param id
     */
    public void setSwingSound(FormID id) {
        TNAM.setForm(id);
    }

    /**
     *
     * @return
     */
    public FormID getSwingSound() {
        return TNAM.getForm();
    }

    /**
     *
     * @param id
     */
    public void setBoundWeaponSound(FormID id) {
        UNAM.setForm(id);
    }

    /**
     *
     * @return
     */
    public FormID getBoundWeaponSound() {
        return UNAM.getForm();
    }

    /**
     *
     * @param in
     */
    public void setWeaponType(WeaponType in) {
        DNAM.wtype = in;
    }

    /**
     *
     * @return
     */
    public WeaponType getWeaponType() {
        return DNAM.wtype;
    }

    /**
     *
     * @param speed
     */
    public void setSpeed(float speed) {
        DNAM.speed = speed;
    }

    /**
     *
     * @return
     */
    public float getSpeed() {
        return DNAM.speed;
    }

    /**
     *
     * @param reach
     */
    public void setReach(float reach) {
        DNAM.reach = reach;
    }

    /**
     *
     * @return
     */
    public float getReach() {
        return DNAM.reach;
    }

    /**
     *
     * @param fov
     */
    public void setSightFOV(float fov) {
        DNAM.sightFOV = fov;
    }

    /**
     *
     * @return
     */
    public float getSightFOV() {
        return DNAM.sightFOV;
    }

    /**
     *
     * @param vats
     */
    public void setVATS(int vats) {
        DNAM.vats = vats;
    }

    /**
     *
     * @return
     */
    public int getVATS() {
        return DNAM.vats;
    }

    /**
     *
     * @param numProj
     */
    public void setNumProjectiles(int numProj) {
        DNAM.numProjectiles = numProj;
    }

    /**
     *
     * @return
     */
    public int getNumProjectiles() {
        return DNAM.numProjectiles;
    }

    /**
     *
     * @param minRange
     */
    public void setMinRange(float minRange) {
        DNAM.minRange = minRange;
    }

    /**
     *
     * @return
     */
    public float getMinRange() {
        return DNAM.minRange;
    }

    /**
     *
     * @param maxRange
     */
    public void setMaxRange(float maxRange) {
        DNAM.maxRange = maxRange;
    }

    /**
     *
     * @return
     */
    public float getMaxRange() {
        return DNAM.maxRange;
    }

    /**
     *
     * @param stagger
     */
    public void setStagger(float stagger) {
        DNAM.stagger = stagger;
    }

    /**
     *
     * @return
     */
    public float getStagger() {
        return DNAM.stagger;
    }

    /**
     *
     * @param critDmg
     */
    public void setCritDamage(int critDmg) {
        CRDT.critDmg = critDmg;
    }

    /**
     *
     * @return
     */
    public int getCritDamage() {
        return CRDT.critDmg;
    }

    /**
     *
     * @param critMult
     */
    public void setCritMult(float critMult) {
        CRDT.critMult = critMult;
    }

    /**
     *
     * @return
     */
    public float getCritMult() {
        return CRDT.critMult;
    }

    /**
     *
     * @param onDeath
     */
    public void setCritEffectOnDeath(boolean onDeath) {
        if (onDeath) {
            CRDT.onDeath = 1;
        } else {
            CRDT.onDeath = 0;
        }
    }

    /**
     *
     * @return
     */
    public boolean getCritEffectOnDeath() {
        if (CRDT.onDeath == 0) {
            return false;
        } else {
            return true;
        }
    }

    /**
     *
     * @param critEffect
     */
    public void setCritEffect(FormID critEffect) {
        CRDT.critEffect = critEffect;
    }

    /**
     *
     * @return
     */
    public FormID getCritEffect() {
        return CRDT.critEffect;
    }

    /**
     *
     * @param flag
     * @param on
     */
    public void set(WeaponFlag flag, boolean on) {
        switch (flag.flagSet) {
            case 0:
                DNAM.flags1.set(flag.value, on);
                break;
            case 1:
                DNAM.flags2.set(flag.value, on);
                break;
        }
    }

    /**
     *
     * @param flag
     * @return
     */
    public boolean get(WeaponFlag flag) {
        switch (flag.flagSet) {
            case 0:
                return DNAM.flags1.get(flag.value);
            case 1:
                return DNAM.flags2.get(flag.value);
            default:
                return false;
        }
    }

    /**
     *
     * @param weap
     */
    public void setTemplate(FormID weap) {
        CNAM.setForm(weap);
    }

    /**
     *
     * @return
     */
    public FormID getTemplate() {
        return CNAM.getForm();
    }
}
